import { SYSTEM, DECORATORNAME } from '../common/constant';
import { MethodDecoratorStateManager } from '../common/statemanager';
import { MethodDecoratorValidator } from '../common/validator';
import { DecoratedClassOrProto } from '../../decorator';
import { DecoratorFactory } from '../DecoratorFactory';
import { DecoratorInfo } from '../DecoratorInfo';
import { Inject } from '../ioc';
import { AdviceFactory } from './Advices';
import { AspectDecoratorStateManager } from './AspectDecoratorStateManager';
import { PointCutDecoratorConfigHandler } from './PointCutDecoratorConfigHandler';
import { PointCutDecoratorConfig, InstancePointCut, AdviceType, AdviceMethod, AdviceItem } from '../../aop';
import { i18n } from '@/i18n/i18n';

/**
 * 所有切入点装饰器
 */
export class PointCutDecoratorFactory extends DecoratorFactory {
  /**
   * 装饰器信息
   */
  protected decoratorInfo!: DecoratorInfo;

  /**
   * 装饰器配置
   */
  protected decoratorConfig!: PointCutDecoratorConfig;

  /**
   * 方法装饰器校验器
   */
  @Inject({
    module: SYSTEM.LIB,
    ctor: MethodDecoratorValidator,
  })
  protected decoratorValidator!: MethodDecoratorValidator;

  /**
   * 装饰器配置处理器
   */
  @Inject({
    module: SYSTEM.LIB,
    ctor: PointCutDecoratorConfigHandler,
  })
  protected configHandler!: PointCutDecoratorConfigHandler;

  /**
   * 方法状态管理器
   */
  @Inject({
    module: SYSTEM.LIB,
    ctor: MethodDecoratorStateManager,
  })
  protected stateManager!: MethodDecoratorStateManager;

  /**
   * aspect装饰状态管理器
   */
  @Inject({
    module: SYSTEM.LIB,
    ctor: AspectDecoratorStateManager,
  })
  protected aspectStateManager!: AspectDecoratorStateManager;

  /**
   * 初始化装饰器信息
   */
  protected initDecoratorInfo(decoratorName: string | symbol): void {
    this.decoratorInfo = new DecoratorInfo()
      .setName(decoratorName)
      .setConflictList([
        DECORATORNAME.AFTER,
        DECORATORNAME.BEFORE,
        DECORATORNAME.AROUND,
        DECORATORNAME.AFTERRETURN,
        DECORATORNAME.AFTERTHROW,
        DECORATORNAME.POINTCUT,
      ]);
  }

  /**
   * 校验装饰器
   * @param target 被装饰的类或类原型
   * @param propertyKey 被装饰的方法
   */
  protected validateDecorator(target: DecoratedClassOrProto, propertyKey: string | symbol): void {
    // 获取冲突列表
    const { conflictList } = this.decoratorInfo;
    // 校验冲突
    if (this.decoratorValidator.isDecoratorConflict(target, conflictList, propertyKey)) {
      throw new Error(i18n.ERROR.DECORATOER_CONFLICT);
    }
  }

  /**
   * 配置检查
   * @param config 切点配置
   */
  protected preCheckConfig(config: PointCutDecoratorConfig): void {}

  /**
   * 预处理配置
   * @param config  配置
   * @param target
   */
  protected preHandleConfig(config: PointCutDecoratorConfig, target?: DecoratedClassOrProto) {
    // 默认配置
    let defaultConfig: InstancePointCut = {
      module: /^.*$/,
      ctor: /^.*$/,
      method: /^.*$/,
    };
    let pointCutConfig = {};
    // 是切点函数
    if (typeof config === 'function') {
      config = config();
    }
    // 是切点表达式
    if (typeof config === 'string') {
      // 解析切点表达式
      config = this.configHandler.parsePointCutExpWithMemo(config) as any;
    }
    // 是配置对象
    if (typeof config === 'object') {
      // 若配置中有切点表达式则进行解析
      let pointCutConfig = {};
      if ('expression' in config) {
        pointCutConfig = this.configHandler.parsePointCutExpWithMemo(config.expression);
        delete (config as any).expression;
        config = {
          ...pointCutConfig,
          ...config,
        };
      }
      // 合并配置
      defaultConfig = {
        ...defaultConfig,
        ...(config as any),
      };
    }
    return defaultConfig;
  }

  /**
   * 将原始方法包装为通知方法
   * @param pointCut 切点对象
   * @param activeType 通知类型
   * @param method  原始方法
   * @returns 通知项
   */
  protected wrapAdviceMethod(pointCut: InstancePointCut, activeType: AdviceType, method: AdviceMethod): AdviceItem {
    // 包装通知
    let adviceFunc = AdviceFactory.getAdvice(activeType, method);
    let adviceItem = { pointCut, advice: adviceFunc };
    return adviceItem;
  }

  /**
   * 状态设置
   * @param target 被装饰的类或原型
   * @param propertyKey 被装饰的方法
   */
  protected setupState(
    target: DecoratedClassOrProto,
    propertyKey: string | symbol,
    activeType: AdviceType,
    adviceItem: AdviceItem,
  ): void {
    // 设置配置
    this.decoratorInfo.setConfig(this.decoratorConfig);
    // 注册装饰器信息到方法
    this.stateManager.setDecoratorInfo(target, this.decoratorInfo, propertyKey);
    // 初始化类通知列表
    if (!this.aspectStateManager.hasAdvices(target)) {
      this.aspectStateManager.setAdvices(target);
    }
    this.aspectStateManager.setAdviceOfType(target, activeType, adviceItem);
  }

  /**
   * 创建装饰器
   * @param config 装饰器配置
   * @param decoratorName 装饰器名称
   */
  public createDecorator(
    config: PointCutDecoratorConfig,
    decoratorName: symbol | string,
    adviceType: AdviceType,
  ): MethodDecorator {
    return (target: DecoratedClassOrProto, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
      // 初始化装饰器
      this.initDecoratorInfo(decoratorName);
      // 校验装饰器
      this.validateDecorator(target, propertyKey);
      // 前置配置检查
      this.preCheckConfig(config);
      // 预处理配置
      const pointCutObj = this.preHandleConfig(config);
      // 包装通知方法
      const adviceItem = this.wrapAdviceMethod(pointCutObj, adviceType, descriptor.value);
      // 设置状态
      this.setupState(target, propertyKey, adviceType, adviceItem);
    };
  }
}
